<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
 * @author  Philip Sturgeon
 * @created 9 Dec 2008
 */
class Apns
{

    private   $certificate  = "/www/wwwroot/app.zclxing.com/cert/apns-dis.pem";
    private   $passphrase   = '123456';
    private   $link         = 'ssl://gateway.push.apple.com:2195';
    // private   $sandbox_link = 'ssl://gateway.sandbox.push.apple.com:2195';
    protected $CI;

    public function __construct()
    {
        $this->CI =& get_instance();
        $this->CI->load->model('apns_m');
    }

    // function send($deviceTokens, $msg){
    //     $fp = $this->create_apns_link();
    //     $body['aps'] = array(
    //         'alert' => $msg ,
    //         'badge' => 1
    //     );
    //     $apple_expiry = time()+86400;
    //     $payload = json_encode($body);
    //     $i = 1;
    //     //推送开始
    //     foreach ($deviceTokens as $v){
    //         $msg = pack("C", 1) . pack("N", $i) . pack("N", $apple_expiry) . pack("n", 32) . pack('H*', str_replace(' ', '', $v)) . pack("n", strlen($payload)) . $payload;
    //         if( !fwrite($fp, $msg) || $this->checkAppleErrorResponse($fp) ){
    //             //如果失败,则进行重发,连续3次还是失败,则重新建立连接,并对下一个devicetoken进行推送
    //             $times = 1;
    //             while($times <= 3){
    //                 if(!fwrite($fp, $msg) || $this->checkAppleErrorResponse($fp)){
    //                     $times ++ ;
    //                 }else{
    //                     $fp = $this->create_apns_link();
    //                     break;
    //                 }
    //             }
    //         }
    //         //每隔100次重新和APNS服务器建立连接.
    //         if( $i%100 == 0 ){
    //             $fp = $this->create_apns_link();
    //         }
    //         $i++;
    //     }
    //     usleep(500000);
    //     $this->checkAppleErrorResponse($fp);
    //     fclose($fp);
    // }
    //
    // private function create_apns_link(){
    //
    //     $scc = stream_context_create();
    //     stream_context_set_option($scc, 'ssl', 'local_cert', $this->certificate);
    //     stream_context_set_option($scc, 'ssl', 'passphrase', $this->passphrase);
    //     $fp = stream_socket_client($this->sandbox_link, $err,$errstr, 60, STREAM_CLIENT_CONNECT, $scc);
    //     $i = 0;
    //     while (gettype($fp) != 'resource'){
    //         //echo 'rebuild<br/>';
    //         if($i < 3){
    //             usleep(500000);
    //             $fp = stream_socket_client($this->sandbox_link, $err,$errstr, 60, STREAM_CLIENT_CONNECT, $scc);
    //         }else{
    //             break;
    //         }
    //     }
    //     stream_set_blocking ($fp, 0);
    //     if($fp){
    //         echo 'Connected to APNS for Push Notification' . '<br/>';
    //     }
    //     return $fp;
    //     exit();
    // }
    //
    //
    //
    // function checkAppleErrorResponse($fp) {
    //
    //     //byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID).
    //     // Should return nothing if OK.
    //     //NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait
    //     // forever when there is no response to be sent.
    //     $apple_error_response = fread($fp, 6);
    //     if ($apple_error_response) {
    //         // unpack the error response (first byte 'command" should always be 8)
    //         $error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);
    //         if ($error_response['status_code'] == '0') {
    //             $error_response['status_code'] = '0-No errors encountered';
    //
    //         } else if ($error_response['status_code'] == '1') {
    //             $error_response['status_code'] = '1-Processing error';
    //
    //         } else if ($error_response['status_code'] == '2') {
    //             $error_response['status_code'] = '2-Missing device token';
    //
    //         } else if ($error_response['status_code'] == '3') {
    //             $error_response['status_code'] = '3-Missing topic';
    //
    //         } else if ($error_response['status_code'] == '4') {
    //             $error_response['status_code'] = '4-Missing payload';
    //
    //         } else if ($error_response['status_code'] == '5') {
    //             $error_response['status_code'] = '5-Invalid token size';
    //
    //         } else if ($error_response['status_code'] == '6') {
    //             $error_response['status_code'] = '6-Invalid topic size';
    //
    //         } else if ($error_response['status_code'] == '7') {
    //             $error_response['status_code'] = '7-Invalid payload size';
    //
    //         } else if ($error_response['status_code'] == '8') {
    //             $error_response['status_code'] = '8-Invalid token';
    //
    //         } else if ($error_response['status_code'] == '255') {
    //             $error_response['status_code'] = '255-None (unknown)';
    //
    //         } else {
    //             $error_response['status_code'] = $error_response['status_code'].'-Not listed';
    //
    //         }
    //         return $error_response;
    //     }
    //     return false;
    // }

    public function send($deviceTokens, $data)
    {
        $fp = $this->create_apns_link();

        $body['aps']  = [
            'alert' => $data['msg'],
            'badge' => 1,
        ];
        if($data['code'] == 1){
            $body['aps']['sound'] = 'NewOrder.mp3';
        }else{
            $body['aps']['sound'] = 'default';
        }
        $body['order']  = [
            'order_id' => $data['order_id'],
            'code' => $data['code'],
        ];
        $apple_expiry = 0;
        $payload      = json_encode($body);
        $num          = count($deviceTokens);
        for ($i = 0; $i < $num; $i++) {
            $deviceToken = $deviceTokens[$i];
            $msg         = pack("C", 1) . pack("N", $i) . pack("N", $apple_expiry) . pack("n", 32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n", strlen($payload)) . $payload;
            // $rtn         = fwrite($fp, $msg);
            // usleep(400000);//每次推送过后，因为php是同步的apns服务器不会立即返回错误消息，所以这里等待400ms
            while (!fwrite($fp, $msg)) {
                usleep(400000); //400 msec
            }
            $errorcode = $this->checkAppleErrorResponse($fp);//检查是否存在错误消息
            if ($errorcode) {
                if ($errorcode == '8') {//如果令牌无效就删除
                    $this->CI->apns_m->update(['device_token' => $deviceToken], ['deleted' => 1]);
                }
                if ($i < $num - 1) {//如果还没推送完，需要重新建立连接推送后面的
                    $fp = $this->create_apns_link();
                }
            }
        }
        fclose($fp);
        return true;
    }

    private function create_apns_link()
    {
        $scc = stream_context_create();
        stream_context_set_option($scc, 'ssl', 'local_cert', $this->certificate);
        stream_context_set_option($scc, 'ssl', 'passphrase', $this->passphrase);
        // $fp = stream_socket_client($this->sandbox_link, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $scc);
        $fp = stream_socket_client($this->link, $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $scc);
        $i  = 0;
        while (gettype($fp) != 'resource') {//如果建立失败就每隔100ms重新建立一次，如果失败3次就放弃
            if ($i < 3) {
                usleep(100000);
                $fp = stream_socket_client($this->link, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $scc);
                $i++;
            } else {
                break;
            }
        }
        stream_set_blocking($fp, 0);
        if (!$fp) {
            echo 'Connected to APNS for Push Notification';
            exit();
        }
        return $fp;
    }

    private function checkAppleErrorResponse($fp)
    {
        //byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID).
        // Should return nothing if OK.
        //NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait
        // forever when there is no response to be sent.
        $apple_error_response = fread($fp, 6);
        if ($apple_error_response) {

            // unpack the error response (first byte 'command" should always be 8)
            $error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);

            return $error_response['status_code'];
        }
        return false;
    }
}